﻿

using System.Diagnostics;

namespace SharpTalk.Services
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Runtime.Remoting.Messaging;
    using System.ServiceModel;
    using System.Threading.Tasks;
    using Core;
    using Core.Delegates;
    using Core.Enums;
    using Core.Exceptions;
    using Core.Interfaces;
    using Core.Resources;
    using Core.Services;
    using Core.Services.Contracts.Server;
    using Core.Services.Requests;
    using Core.Services.Responses;

    [ServiceBehavior(Namespace = "http://SharpTalk/", ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    public sealed class UserServices :
        RequestServiceBase<
            UserCreateRequest, UserCreateResponse,
            UserFindRequest, UserFindResponse,
            UserUpdateRequest, UserUpdateResponse,
            UserDeleteRequest, UserDeleteResponse>,
        IUserServices
    {
        private static IUserManager _manager = null;

        private UserServices(){}

        public UserServices(IUserManager manager) 
            : base()
        {
            _manager = manager;
        }

        public IUserManager Manager { get { return _manager; } }

        public override UserCreateResponse Create(UserCreateRequest request)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            try
            {
                CheckRequest(request);

                var user = _manager.Create(
                                        request.Content.FirstName,
                                        request.Content.LastName,
                                        request.Content.NickName,
                                        request.Content.Image,
                                        request.Content.Uri);
                return new UserCreateResponse() { Content = user };
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
                return new UserCreateResponse() { Error = new ErrorDetails(exp)  };
            }
        }

        public override UserFindResponse Find(UserFindRequest request)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            try
            {
                CheckRequest(request);
                
                var users = request.Content.Guid == Guid.Empty 
                    ? _manager.Find( request.Content.FirstName, request.Content.LastName, request.Content.NickName)
                    : new List<User>() { _manager.Find(request.Content.Guid) }; 

                return new UserFindResponse() { Content = users };
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
                return new UserFindResponse() { Error = new ErrorDetails(exp) };
            }
        }

        public override UserUpdateResponse Update(UserUpdateRequest request)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            try
            {
                CheckRequest(request);

                var exist = _manager.Find(request.Content.User.Guid);
                if(exist == null)
                    throw new UserException(Messages.user_not_found);

                var success = _manager.Update(request.Content.User) !=null;

                if(success)
                {
                    TraceDebug("Notifying user {0:N} changed.",request.Content.User.Guid);
                    
                    var notificationRequest = new MessageSendRequest()
                                                {
                                                    Owner = UserService.GetSystemUser().Guid,
                                                    Content = new MessageSendRequestDetails()
                                                                {
                                                                    Type = MessageTypeEnum.UserChange,
                                                                    Message = new UserNotificationMessage()
                                                                                    {
                                                                                        Owner = UserService.GetSystemUser().Guid,
                                                                                        Content = request.Content.User
                                                                                    }
                                                                }
                                                };

                    Task.Factory.StartNew(()=> MessageServices.Instance.SendMessage(notificationRequest));
                }
                var response = new UserUpdateResponse()
                           {
                               Content = success ? request.Content.User : _manager.Find(request.Content.User.Guid) ,
                               Error = success ? null : new ErrorDetails(new UserException(Messages.user_update_failed))
                           };

                return response;
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
                return new UserUpdateResponse()
                           {
                               Error = new ErrorDetails(exp)
                           };
            }
        }

        public override UserDeleteResponse Delete(UserDeleteRequest request)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            try
            {
                CheckRequest(request);

                return new UserDeleteResponse() { Content = _manager.Delete( request.Content.Guid ) };
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
                return new UserDeleteResponse() { Error = new ErrorDetails(exp) };
            }
        }

        private UserNotifyChangedResponse NotifyUserChange(UserNotifyChangedRequest request)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            try
            {
                CheckRequest(request, ()=> request!=null && request.Owner!= Guid.Empty && request.Content.Guids != null );

                var tracker = _manager.Find(request.Owner);
                if(tracker != null) 
                    _manager.TrackUserChanges( request.Content.Guids, tracker );

                return new UserNotifyChangedResponse(){ Content = tracker !=null };
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
                return new UserNotifyChangedResponse()
                           {
                               Content = false,
                               Error = new ErrorDetails(exp)
                           };
            }
        }
        public IAsyncResult BeginNotifyUserChange(UserNotifyChangedRequest request, AsyncCallback callback, object state)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var del = new RequestDelegate<UserNotifyChangedRequest, UserNotifyChangedResponse>(NotifyUserChange);
            return del.BeginInvoke(request, callback, state);
        }
        public UserNotifyChangedResponse EndNotifyUserChange(IAsyncResult result)
        {
            if (ShouldTrace(TraceEventType.Verbose)) TraceMethodCall(MethodBase.GetCurrentMethod());
            var res = (AsyncResult) result;
            var ret = ((RequestDelegate<UserNotifyChangedRequest, UserNotifyChangedResponse>) res.AsyncDelegate).
                EndInvoke(result);
            res.AsyncWaitHandle.Close();
            return ret;
        }
    }
}